// @(#)Port.java   2/2004
// Copyright (c) 1998-2004, Distributed Real-time Computing Lab (DRCL) 
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer. 
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution. 
// 3. Neither the name of "DRCL" nor the names of its contributors may be used
//    to endorse or promote products derived from this software without specific
//    prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 

package drcl.comp;

import java.util.*;
import drcl.data.*;
import drcl.comp.contract.*;

/**
 * The implementation of a port in the autonomous component architecture (ACA).
 * A port has separate input and output channels.  Each channel can be
 * connected to a different {@link Wire}.  The wire that is connected to the
 * input channel is called the "in-wire" of the port.  The wire that is 
 * connected to the output channel is called the "out-wire" of the port.
 * Use {@link #getInWire()} and {@link #getOutWire()} to access those wires.
 *
 * @see Component
 * @see Wire
 */
public class Port extends drcl.DrclObj implements Handler
{
  /** Set to true to make ports created from now execution boundary.*/
  public static boolean EXECUTION_BOUNDARY = true;

  /** The duplex port type. */
  public final static int PortType_INOUT = 0;
  /** The input-only port type. */
  public final static int PortType_IN = 1;
  /** The output-only port type. */
  public final static int PortType_OUT = 2;
  /** The server port type */
  public final static int PortType_SERVER = 3;
  /** The event port type */
  public final static int PortType_EVENT = 4;
  /** The <i>fork</i> port type */
  public final static int PortType_FORK = 5;

  public static final Object SEND_RCV_REQUEST = "SEND_RCV_REQ";

  final static String[] PortType_ALL = new String[]{
    "IN&OUT", "IN", "OUT", "SERVER", "EVENT", "FORK"
  };

  // Mask of the port type encoded in the port flag, default is PortType_INOUT
  static final int Flag_TYPE        = 7; // first 3 bits
  // Bit mask of the "execution boundary" flag, default is on
  // Turn off the flag to have the thread which delivers data to the port
  // continue processing the data in the host component.
  static final int Flag_EXEBOUNDARY = 1 << 3;
  // Bit mask of the "removable" flag, default is on
  static final int Flag_REMOVABLE   = 1 << 4;
  // Bit mask of the "shadow port" flag, default is off
  static final int Flag_SHADOW      = 1 << 5;
  // Bit mask of the "trace" flag, default is off
  static final int Flag_TRACE_SEND  = 1 << 6;
  //static final int Flag_TRACE_DATA  = 1 << 7;
  // Bit mask of the "event export" flag, default is on
  static final int Flag_EVENT_EXPORT= 1 << 8;
  
  /**
   ID of the port group this port belongs to.
   Must use {@link #setGroupID(String)} or {@link #set(String, String)} to
   set the group ID of a port
   because it affects the bookkeeping in the host component.
   */
  public String groupID;
  
  /**
   ID of the port, unique in the port group it belongs to.
   Must use {@link #setID(String)} or {@link #set(String, String)} to set
   the ID of a port
   because it affects the bookkeeping in the host component.
   @see #groupID
   */
  public String id;
  
  // The component that contains the port.
  public Component host;
  
  protected Wire outwire, inwire;
  
  
  // By default, exe boundary, removable and event export
  int flag = EXECUTION_BOUNDARY?
      (Flag_EXEBOUNDARY | Flag_REMOVABLE | Flag_EVENT_EXPORT):
      (Flag_REMOVABLE | Flag_EVENT_EXPORT);

  // separate this from other flags for performance reason
  boolean flagTraceData = false;
  
  private Handler handler = this;

  //
  private void ___CONSTRUCTOR_INIT___() {}
  //

  public static class Builder {
    private Port port = new Port();;

    public Builder setType(int type_) {
      port.setType(type_);
      return this;
    }

    public Builder setHandler(Handler h) {
      port.handler = h;
      return this;
    }

    public Builder setExecutionBoundary(boolean b) {
      port.setExecutionBoundary(b);
      return this;
    }

    public Port create() {
      return port;
    }
  }
  
  /** Constructor, default duplex port type. */
  public Port()
  { this(PortType_INOUT);  }
  
  /** Constructor, with specified port type. */
  public Port(int type_)
  { setType(type_);  }

  /** Constructor, with specified port type and properties. */
  public Port(int type_, boolean exeBoundary_)
  {
    this(type_);
    if (!exeBoundary_) flag = flag & ~Flag_EXEBOUNDARY;
  }
  
  public final void reset()
  {
  }

  void setHandler(Handler h) {
    handler = h;
  }
  
  //
  private void ___PROPERTIES___() {}
  //
  
  /** Sets the host component. */
  public final void setHost(Component host_)
  { host = host_;  }
  
  /** Returns the host component. */
  public final Component getHost()
  { return host; }
  
  /**
   * Sets the id of the group this port belongs to.
   * Returns false if failed.
   */
  public final boolean setGroupID(String groupID_) 
  { 
    if (id == null) {
      groupID = groupID_== null? null: groupID_.intern();
      return true;
    }
    else return _set(groupID_, id); 
  }
  
  /** Get the id of the group this port belongs to. */
  public final String getGroupID()
  { return groupID; }
  
  /**
   * Set the id of the port, unique in the port group it belongs to.
   * Returns false if failed.
   */
  public final boolean setID(String id_) 
  { 
    if (groupID == null) {
      id = id_== null? null: id_.intern();
      return true;
    }
    else return _set(groupID, id_); 
  }
  
  /** Sets the group ID and port ID of this port. */
  public final boolean set(String gid_, String id_)
  {
    if (gid_ != null) gid_ = gid_.intern();
    if (id_ != null) id_ = id_.intern();
    return _set(gid_, id_);
  }

  boolean _set(String gid_, String id_)
  {
    if (host != null && (gid_ == null || id_ == null)) return false;
    
    if (host == null || gid_ == null || id == null) {
      groupID = gid_; id = id_;
      return true;
    }
    else {
      if (host.getPort(gid_, id_) != null) return false;
      boolean removable_ = isRemovable();
      if (!removable_) setRemovable(true);
      host.removePort(this);
      host.addPort(this, gid_, id_);
      if (!removable_) setRemovable(false);
      return true;
    }
  }
  
  /** Returns the id of the port. */
  public final String getID()
  { return id; }
  
  public final void setType(int type_) 
  { 
    flag &= ~Flag_TYPE;
    flag |= type_;
  }

  public final void setType(String type_) 
  {
    for (int i=0; i<PortType_ALL.length; i++)
      if (type_.equals(PortType_ALL[i])) {
        setType(i);
        return;
      }
  }

  /** Returns the port type of this port. */
  public final String getTypeInString()
  {
    try {
      return PortType_ALL[flag & Flag_TYPE];
    }
    catch (Exception e_) {
      return "UNKNOWN";
    }
  }

  public final int getType()
  {
    return flag & Flag_TYPE;
  }
  
  public final void setExecutionBoundary(boolean flag_) 
  { 
    if (flag_) flag |= Flag_EXEBOUNDARY; 
    else flag &= ~Flag_EXEBOUNDARY;
  }
  
  public final boolean isExecutionBoundary() 
  { return (flag & Flag_EXEBOUNDARY) != 0;  }
  
  public final int getFlag()
  { return flag; }
  
  public final void setRemovable(boolean flag_) 
  { 
    if (flag_) flag |= Flag_REMOVABLE; 
    else flag &= ~Flag_REMOVABLE;
  }
  
  public final boolean isRemovable() 
  { return (flag & Flag_REMOVABLE) != 0;  }
  
  public final void setTraceEnabled(boolean enabled_) 
  { 
    if (!isShadow())
      flagTraceData = enabled_;
    if (enabled_) {
      if (isShadow() && !flagTraceData) {
      //if ((flag & Flag_TRACE_DATA) == 0) {
      //  flag |= Flag_TRACE_DATA; 
        flagTraceData = enabled_;
        if (inwire != null)
          inwire.inEvtListeners = new PortPack(this,
                  inwire.inEvtListeners);
      }
      if ((flag & Flag_TRACE_SEND) == 0) {
        flag |= Flag_TRACE_SEND; 
        if (outwire != null)
          outwire.outEvtListeners = new PortPack(this,
                  outwire.outEvtListeners);
      }
    }
    else {
      if (isShadow() && flagTraceData) {
      //if ((flag & Flag_TRACE_DATA) != 0) {
      //  flag &= ~Flag_TRACE_DATA;
        flagTraceData = enabled_;
        if (inwire != null)
          inwire.inEvtListeners = 
            inwire._removePort(inwire.inEvtListeners, this);
      }
      if ((flag & Flag_TRACE_SEND) != 0) {
        flag &= ~Flag_TRACE_SEND;
        if (outwire != null)
          outwire.outEvtListeners = 
            outwire._removePort(outwire.outEvtListeners, this);
      }
    }
  }
  
  public final void setDataTraceEnabled(boolean enabled_) 
  { 
    if (!isShadow())
      flagTraceData = enabled_;
    //else if (enabled_ ^ (flag & Flag_TRACE_DATA) != 0) {
    else if (enabled_ ^ flagTraceData) {
    if (enabled_) {
    //  flag |= Flag_TRACE_DATA; 
      flagTraceData = enabled_;
      if (inwire != null)
        inwire.inEvtListeners = new PortPack(this,
                inwire.inEvtListeners);
    }
    else {
    //  flag &= ~Flag_TRACE_DATA;
      flagTraceData = enabled_;
      if (inwire != null)
        inwire.inEvtListeners = 
          inwire._removePort(inwire.inEvtListeners, this);
    }
    }
  }
  
  public final boolean isDataTraceEnabled() 
  //{ return (flag & Flag_TRACE_DATA) != 0;  }
  { return flagTraceData; }

  public final void setSendTraceEnabled(boolean enabled_) 
  { 
    if (enabled_ ^ (flag & Flag_TRACE_SEND) != 0) {
    if (enabled_) {
      flag |= Flag_TRACE_SEND; 
      if (outwire != null)
        outwire.outEvtListeners = new PortPack(this,
                outwire.outEvtListeners);
    }
    else {
      flag &= ~Flag_TRACE_SEND;
      if (outwire != null)
        outwire.outEvtListeners = 
          outwire._removePort(outwire.outEvtListeners, this);
    }
    }
  }
  
  public final boolean isSendTraceEnabled() 
  { return (flag & Flag_TRACE_SEND) != 0;  }

  public final void setEventExportEnabled(boolean enabled_) 
  { 
    if (enabled_) flag |= Flag_EVENT_EXPORT; 
    else flag &= ~Flag_EVENT_EXPORT;
  }
  
  public final boolean isEventExportEnabled() 
  { return (flag & Flag_EVENT_EXPORT) != 0;  }

  public final boolean isShadow()
  { return (flag & Flag_SHADOW) != 0;  }
  
  public void setShadow(boolean flag_)
  {
    if (flag_ ^ (flag & Flag_SHADOW) != 0) {
    if (flag_ ^ anyClient()) {
      if (flag_)
        throw new PortException("Port has no client to become shadow: "
                + this);
      else
        throw new PortException("Port has at least one client.  "
                + "Cannot become nonshadow: " + this);
    }
    _setShadow(flag_);
    }
  }
  void _setShadow(boolean flag_)
  {
    if (flag_) {
      if (inwire != null)
        inwire._moveToShadow(this);
      if (outwire != inwire && outwire != null)
        outwire._moveToShadow(this);
      flag |= Flag_SHADOW; 
    }
    else {
      if (inwire != null)
        inwire._moveOutOfShadow(this);
      if (outwire != inwire && outwire != null)
        outwire._moveOutOfShadow(this);
      flag &= ~Flag_SHADOW;
    }
  }
  
  public final Contract getContract()
  {
    if (anyClient()) { // this port is a shadow port
      Port[] pp_ = getConceptualClients();
      Vector vContract_ = new Vector();
      for (int i=0; i<pp_.length; i++)
        if (pp_[i] != null) vContract_.addElement(pp_[i].getContract());
      if (vContract_.size() == 0)
        return ContractAny.INSTANCE;
      else if (vContract_.size() == 1)
        return (Contract)vContract_.firstElement();
      else {
        // create a multiple-contract
        Contract[] cc_ = new Contract[vContract_.size()];
        vContract_.copyInto(cc_);
        return new ContractMultiple(cc_);
      }
    }
    else {
      return Component.getContract(this);
    }
  }
  
  public Wire getOutWire()
  { return outwire; }
  
  public Wire getInWire()
  { return inwire; }
  
  /** Returns true if the port connects to or shadows for at least one port.
   */
  public final boolean anyConnection() 
  {
    if (outwire != null && outwire.anyPortExcept(this)) return true;
    if (inwire != null && inwire != outwire)
      return inwire.anyPortExcept(this);
    return false;
  }
  
  /** Returns true if the port connects to or shadows for at least one port.
   */
  public final boolean anyOutConnection() 
  {
    if (outwire != null && outwire.anyPortExcept(this)) return true;
    return false;
  }
  
  /**
   * Clients = InClients + OutClients.
   * @see {@link #getInClients()} 
   * @see {@link #getOutClients()} 
   */
  public final boolean anyClient()
  {
    if (outwire == null && inwire == null) return false;
    Port[] tmp_ = null;
    if (outwire != null) {
      tmp_ = outwire.getPortsExcept(this);
      for (int i=0; i<tmp_.length; i++)
        if (host == tmp_[i].host.parent) return true;
    }
    if (inwire != null) {
      tmp_ = inwire.getPortsExcept(this);
      for (int i=0; i<tmp_.length; i++)
        if (host == tmp_[i].host.parent) return true;
    }
    return false;
  }
  
  Port[] _getAllConnectedPorts(boolean removeDuplicate_)
  {
    if (inwire == null && outwire == null) return new Port[0];
    Port[] pp1 = inwire == null? new Port[0]:
      inwire.getPortsExcept(this);
    Port[] pp2 = outwire == null? new Port[0]:
      outwire.getPortsExcept(this);
    return _merge(pp1, pp2, removeDuplicate_);
  }

  // merge pp1 and pp2 to a new array
  // pp1 and pp2 and their elements cannot be null
  // assume pp1 and pp2 do not contain duplicates 
  static Port[] _merge(Port[] pp1, Port[] pp2, boolean removeDuplicate_)
  {
    if (removeDuplicate_) {
      LinkedList ll = new LinkedList();
      HashMap map_ = new HashMap();
      // put pp1 to map
      for (int i=0; i<pp1.length; i++)
        map_.put(pp1[i], pp1[i]);
      // check if any port in pp2 also appear in pp1
      for (int i=0; i<pp2.length; i++)
        if (!map_.containsKey(pp2[i]))
          ll.add(pp2[i]);
      pp2 = (Port[])ll.toArray(new Port[ll.size()]);
    }
    Port[] tmp_ = new Port[pp1.length + pp2.length];
    System.arraycopy(pp1, 0, tmp_, 0, pp1.length);
    System.arraycopy(pp2, 0, tmp_, pp1.length, pp2.length);
    return tmp_;
  }
  
  /**
   * Clients = InClients + OutClients.
   * @see {@link #getInClients()} 
   * @see {@link #getOutClients()} 
   */
  public final Port[] getClients()
  {
    if (inwire == null && outwire == null) return new Port[0];
    Port[] in_ = getInClients();
    Port[] out_ = getOutClients();
    return _merge(in_, out_, true);
  }
  
  /**
   * OutClients: the set of ports at which if data is sent, 
   * the data would arrive at this port.
   */
  public final Port[] getOutClients()
  {
    if (outwire == null) return new Port[0];
    Port[] tmp_ = outwire.getOutPorts();
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (host.isAncestorOf(p_.host))
        v_.addElement(p_);
    }
    tmp_ = new Port[v_.size()];
    v_.copyInto(tmp_);
    return tmp_;
  }
  
  /**
   * InClients: the set of ports at which if data is sent, 
   * the data would arrive at this port.
   */
  public final Port[] getInClients()
  {
    if (inwire == null) return new Port[0];
    Port[] tmp_ = inwire.getInPorts();
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (host.isAncestorOf(p_.host))
        v_.addElement(p_);
    }
    tmp_ = new Port[v_.size()];
    v_.copyInto(tmp_);
    return tmp_;
  }

  /**
   * ConceptualClients = ConceptualOutClients + ConceptualInClients.
   * @see {@link #getConceptualInClients()} 
   * @see {@link #getConceptualOutClients()} 
   */
  public final Port[] getConceptualClients()
  {
    if (inwire == null && outwire == null) return new Port[0];
    Port[] in_ = getConceptualInClients();
    Port[] out_ = getConceptualOutClients();
    return _merge(in_, out_, true);
  }
  
  /**
   * ConceptualOutClients: the OutClients that belong to direct child
   * components.
   */
  public final Port[] getConceptualOutClients()
  {
    if (inwire == null) return new Port[0];
    Port[] tmp_ = outwire.getOutPorts();
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (host == p_.host.parent) 
        v_.addElement(p_);
    }
    tmp_ = new Port[v_.size()];
    v_.copyInto(tmp_);
    return tmp_;
  }

  /**
   * ConceptualInClients: the InClients that belong to direct child
   * components.
   */
  public final Port[] getConceptualInClients()
  {
    if (outwire == null) return new Port[0];
    Port[] tmp_ = inwire.getInPorts();
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (host == p_.host.parent) 
        v_.addElement(p_);
    }
    tmp_ = new Port[v_.size()];
    v_.copyInto(tmp_);
    return tmp_;
  }
  
  
  /** Any peer conntected to this port?  Peer = InPeer or OutPeer.
   * @see {@link #getInPeers()} 
   * @see {@link #getOutPeers()} 
   */
  public final boolean anyPeer()
  {
    if (inwire == null && outwire == null) return false;
    Port[] tmp_ = _getAllConnectedPorts(false);
    for (int i=0; i<tmp_.length; i++)
      if (!host.isAncestorOf(tmp_[i].host)) return true;
    return false;
  }
  
  /**
   * Peers = InPeers + OutPeers.
   * @see {@link #getInPeers()} 
   * @see {@link #getOutPeers()} 
   */
  public final Port[] getPeers()
  {
    if (inwire == null && outwire == null) return new Port[0];
    return _getPeers(_getAllConnectedPorts(true));
  }
  
  /**
   * OutPeers: the set of ports (including shadow ports) at which if data
   * is sent, the data would arrive at this port.
   */
  public final Port[] getOutPeers()
  {
    if (inwire == null) return new Port[0];
    return _getPeers(inwire.getOutPorts());
  }
  
  /**
   * InPeers: if data is sent at this port, the set of
   * ports (including shadow ports) at which the data would arrive. 
   */
  public final Port[] getInPeers()
  {
    if (outwire == null) return new Port[0];
    return _getPeers(outwire.getInPorts());
  }

  // identifies and returns peers of this port (including ancestor ports)
  Port[] _getPeers(Port[] tmp_)
  {
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (p_ == null) System.out.println("Port: oops...");
      if (!host.isAncestorOf(p_.host))
        v_.addElement(p_);
    }
    return (Port[])v_.toArray(new Port[v_.size()]);
  }

  /**
   * ConceptualInPeers: the InPeers except those are hidden by
   * outer components.
   */
  public final Port[] getConceptualInPeers()
  {
    Port[] ancestorsOut = getOutAncestors(); 
    Port[] shadowsIn = outwire == null? new Port[0]:
        outwire.getShadowInPorts(); 
    Port[] connectedTo = getInPeers(); 
    
    // find my shadow out ports
    int nshadow = 0;
    Component outermost = null;
    Component innermost = null;
    for (int i=0; i<ancestorsOut.length; i++) {
      Port p = ancestorsOut[i];
      if (p.host.isAncestorOf(this.host)) {
        if (nshadow == 0)
          outermost = innermost = p.host;
        else if (p.host.isAncestorOf(outermost))
          outermost = p.host;
        else if (innermost.isAncestorOf(p.host))
          innermost = p.host;
        ancestorsOut[nshadow++] = p;
      }
    }

    LinkedList ll = new LinkedList();
    for (int i=0; i<connectedTo.length; i++) {
      Port p = connectedTo[i];
      if (nshadow > 0 && !innermost.isAncestorOf(p.host))
        continue; // dont consider this connection
      boolean pass_ = true;
      for (int j=0; pass_ && j<shadowsIn.length; j++)
        if (shadowsIn[j].host.isAncestorOf(p.host))
          pass_ = false;
      if (pass_) ll.add(p);
    }
    return (Port[])ll.toArray(new Port[ll.size()]);
  }
  
  /** Returns all the ancestor ports (def: the host of which contains
   * the host of this port. */
  public final Port[] getAncestors()
  {
    if (inwire == null && outwire == null) return new Port[0];
    return _getAncestors(_getAllConnectedPorts(true));
  }
  
  /** Returns all the ancestor out ports (def: the host of which contains
   * the host of this port. */
  public final Port[] getOutAncestors()
  {
    if (outwire == null) return new Port[0];
    return _getAncestors(outwire.getShadowOutPorts());
  }
  
  /** Returns all the ancestor in ports (def: the host of which contains
   * the host of this port. */
  public final Port[] getInAncestors()
  {
    if (inwire == null) return new Port[0];
    return _getAncestors(inwire.getShadowInPorts());
  }

  // identifies and returns ancestors of this port
  Port[] _getAncestors(Port[] tmp_)
  {
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (p_.host.isAncestorOf(host))
        v_.addElement(p_);
    }
    return (Port[])v_.toArray(new Port[v_.size()]);
  }
  
  public final Port[] getShadows()
  {
    if (inwire == null && outwire == null) return new Port[0];
       return _getShadows(_getAllConnectedPorts(true));
  }
  
  public final Port[] getOutShadows()
  {
    if (outwire == null) return new Port[0];
       return _getShadows(outwire.getOutPorts());
  }
  
  public final Port[] getInShadows()
  {
    if (inwire == null) return new Port[0];
       return _getShadows(inwire.getInPorts());
  }

  // identifies and returns shadow ports of this
  Port[] _getShadows(Port[] tmp_)
  {
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (p_.host == host.parent)
        v_.addElement(p_);
    }
    return (Port[])v_.toArray(new Port[v_.size()]);
  }
  
  
  /**
   * Returns host's ancestor's peers and the shadow ports.
   */
  public final Port[] getParentPeers()
  {
    if (inwire == null && outwire == null) return new Port[0];
    Port[] tmp_ = _getAllConnectedPorts(true);
    if (tmp_ == null) return new Port[0];
    Vector v_ = new Vector();
    for (int i=0; i<tmp_.length; i++) {
      Port p_ = tmp_[i];
      if (!host.parent.isAncestorOf(p_.host))
        v_.addElement(p_);
    }
    tmp_ = new Port[v_.size()];
    v_.copyInto(tmp_);
    return tmp_;
  }
  
  public boolean isConnectedWith(Port p_)
  {
    if (inwire == null && outwire == null) return false;
    else if (inwire != null && inwire.isAttachedToBy(p_)) return true;
    else if (outwire != null && outwire.isAttachedToBy(p_)) return true;
    else return false;
  }
  
  //
  private void ___DATA_TRANSPORT___() {}
  //
  
  // for Util.inject()
  final void doReceiving(Object data_)
  {
    if (isShadow()) {
      if (isDataTraceEnabled())
        host.trace(Component.Trace_DATA, this, data_, "(shadow)");
      Port[] clients_ = getInClients();
      // do not copy data_
      for (int i=0; i<clients_.length; i++) {
        Port client_ = clients_[i];
        client_.doReceiving(data_);
      }
    }
    else {
      //doReceiving(data_, null, null);
      //if (isDataTraceEnabled())
      //  host.trace(Component.Trace_DATA, this, data_,
      //          "(create context)");
      //Note: trace check is done in TaskReceive  
      host.runtime.newTask(new TaskReceive(this, data_), null);
    }
  }

  /** Called by the host component to send data at this port. */
  public final void doSending(Object data_)
  { doSending(data_, host.runtime.getThread()); }

  //public static boolean DEBUG = false;

  public void process(Object data_, Port inPort_) {
    host.process(data_, inPort_);
  }

  /** Internal implementation of doSending() */
  protected void doSending(Object data_, WorkerThread thread_)
  {
    // server port:
    // bypassing other ports on the out wire of this port.
    if (thread_ != null &&
      thread_.returnPort != null && (flag & Flag_TYPE) == 3) {
      //((Port)thread_.returnPort).doReceiving(data_, this, thread_);

      Port p_ = (Port)thread_.returnPort;
      Component host_ = p_.host;

      // trace for event listeners
      if (outwire != null) {
        for (PortPack tmp_ = outwire.outEvtListeners; tmp_ != null;
                tmp_ = tmp_.next) {
          Port p2_ = tmp_.port;
          if (p2_.host != null && 
            (p2_ == this || p2_.host.isAncestorOf(host)))
            p2_.host.trace(Component.Trace_SEND, p2_, data_);
        }
        for (PortPack tmp_ = p_.outwire.inEvtListeners; tmp_ != null;
              tmp_ = tmp_.next) {
          Port p2_ = tmp_.port;
          if (p2_ == p_ || (p2_.host != null
            && p2_.host.isAncestorOf(host_)))
            p2_.host.trace(Component.Trace_DATA, p2_, data_,
                    "(shadow)");
        }
      }

      // sendReceiving
      if (thread_.sendingPort == p_) {
        //if (p_.isDataTraceEnabled())
        //  host_.trace(Component.Trace_DATA, p_, data_,
        //          "(for sendReceive())");
        thread_.sendingPort = data_;
        return;
      }

      if (thread_.sendingPort == this || !p_.isExecutionBoundary()) {
        if (host_.isEnabled()) {

        //if (p_.isDataTraceEnabled())
        //  host_.trace(Component.Trace_DATA, p_, data_,
        //          "(not across exe boundary)");

        // the following execution order is important
        String prevState_ = thread_.state;
        thread_.totalNumEvents ++;
  
        if (p_.flagTraceData)
          host_.trace(Component.Trace_DATA, p_, data_);
        try {
          p_.handler.process(data_, p_);
        }
        catch (Exception e_) {
          if (host_.isErrorNoticeEnabled()) 
            host_.error(data_, "process()", p_, e_);
          e_.printStackTrace();
        }
  
        thread_.releaseAllLocks(host_);
        thread_.state = prevState_;
        }
      }
      else {// server port: set return port
        //if (p_.isDataTraceEnabled())
        //  host_.trace(Component.Trace_DATA, p_, data_,
        //        "(new context)");
        host_.runtime.newTask(new TaskReceive(p_, data_), thread_);
      }
      return;
    }
    
    if (outwire != null) {
      //outwire.doSending(data_, this);
      
      // trace for event listeners
      for (PortPack tmp_ = outwire.outEvtListeners; tmp_ != null;
              tmp_ = tmp_.next) {
        Port p_ = tmp_.port;
        if (p_ == this)
          p_.host.trace(Component.Trace_SEND, p_, data_);
        else if (p_.host != null && p_.host.isAncestorOf(host))
          p_.host.trace(Component.Trace_SEND, p_, data_,
                  "(shadow)");
      }
      for (PortPack tmp_ = outwire.inEvtListeners; tmp_ != null;
              tmp_ = tmp_.next) {
        Port p_ = tmp_.port;
        if (p_ != this && p_.isShadow() && p_.host != null
          && !p_.host.isAncestorOf(host))
          p_.host.trace(Component.Trace_DATA, p_, data_,
                  "(shadow)");
      }

      for (PortPack tmp_ = outwire.inports; tmp_ != null;
              tmp_ = tmp_.next) {
        Port p_ = tmp_.port;
        Component host_ = p_.host;
        //if (p_ == this || !host_.isEnabled()) continue;
        if (p_ == this) continue;
        //p_.doReceiving(data_, this, thread_);

        if (thread_ == null) {
          //if (p_.isDataTraceEnabled()) 
          //  host_.trace(Component.Trace_DATA, p_, data_,
          //          "(create context)");
          //Note: trace check is done in TaskReceive  
          host_.runtime.newTask(new TaskReceive(p_, data_), thread_);
          continue;
        }

        // sendReceiving
        if (thread_.sendingPort == p_) {
          /*
          if (p_.flagTraceData)
            host_.trace(Component.Trace_DATA, p_, data_,
                    "(for sendReceive())");
          */
          thread_.sendingPort = data_;
          continue;
        }

        // Cross execution boundary if one of the following conditions
        // is true
        // 1. !isExecutionBoundary()
        // 2. thread's sendingPort is peer_
        if (thread_.sendingPort == this || !p_.isExecutionBoundary()) {
          if (host_.isEnabled()) {
          // Not execution boundary, the current thread will process
          // the data.
          // MUST do it out of the synchronized claus.
          // Context needs to be changed beforehand and restored
          // afterwards.
  
          //if (p_.isDataTraceEnabled())
          //  host_.trace(Component.Trace_DATA, p_, data_,
          //          "(not across exe boundary)");
  
          // server port: set return port
          // XXX: we probably need to back up returnPort here
          //      though it is backed up in sendReceive()
          //      because if sending component calls only
          //      doSending() and this port is not an
          //      execution boundary, then the returnPort
          //      is not backed up, so the previous setting
          //      may be overwritten
          if ((p_.flag & Port.Flag_TYPE) == 3)
            thread_.returnPort = this;
    
          // the following execution order is important
          String prevState_ = thread_.state;
          thread_.totalNumEvents ++;
    
          //if (DEBUG && thread_.sendingPort == this)
          //  System.out.println("SEND-RECEIVE: " + this
          //        + " ---> " + p_ + ": data=" + data_);

          if (p_.flagTraceData)
            host_.trace(Component.Trace_DATA, p_, data_);
          try {
            p_.handler.process(data_, p_);
          }
          catch (Exception e_) {
            if (host_.isErrorNoticeEnabled()) 
              host_.error(data_, "process()", p_, e_);
            e_.printStackTrace();
          }
    
          // restore context
          thread_.releaseAllLocks(host_);
            // Don't hold locks across components!
          thread_.state = prevState_;
          }
        }
        else {// server port: set return port
          //if (p_.isDataTraceEnabled())
          //  host_.trace(Component.Trace_DATA, p_, data_,
          //        "(new context)");
          if ((p_.flag & Port.Flag_TYPE) == 3)
            // set up return port
            host_.runtime.newTask(new TaskReceive(p_, data_,
                        this), thread_);
          else
            host_.runtime.newTask(new TaskReceive(p_, data_),
                    thread_);
        }
      } // for (PortPack tmp_...
    }
  }
  
  /**
   * Same as {@link #doSending(Object)} in terms of functionality.
   * Performance-aware components may use
   the runtime
   * notifies the runtime
   */
  public final void doLastSending(Object data_)
  {
    host.finishing();
    doSending(data_, host.runtime.getThread());
  }

  class SendReceiveRunnable implements Runnable
  {
    Port port;
    Object data;

    SendReceiveRunnable(Port port_, Object data_)
    {
      port = port_;
      data = data_;
    }

    public void run()
    {
      data = port.sendReceive(data);
      synchronized (this) {
        this.notify();
      }
    }
  }

  
  public final Object sendReceive(Object data_)
  {
    // the following check is covered by codes below, commented out for reducing overhead
    //if (!anyOutConnection()) 
    //  throw new SendReceiveException("sendreceive()| no connection");
    
    WorkerThread thread_ = host.runtime.getThread();
    if (thread_ == null) {
      //throw new SendReceiveException("sendreceive()| not in a WorkerThread");
      try {
        SendReceiveRunnable srr_ = new SendReceiveRunnable(this, data_);
        synchronized (srr_) {
          host.runtime.addRunnable(0.0, this, srr_);
          srr_.wait();
          return srr_.data;
        }
      }
      catch (Exception e_) {
        e_.printStackTrace();
        throw new SendReceiveException("sendreceive()| " + e_);
      }
    }
    
    Object prevSendingPort_ = thread_.sendingPort;
    thread_.sendingPort = this;

    // store returnPort here in case peer is a server port as well
    Port prevReturnPort_ = thread_.returnPort;
    thread_.returnPort = null;

    doSending(data_, thread_);
    
    if (thread_.sendingPort == this) {
      if (!host.runtime.resetting)
        throw new SendReceiveException("sendreceive()| did not get reply from " 
          + this + ".sendReceive(), data=" + data_);
        //drcl.Debug.error("sendreceive()| did not get reply from " + this
        //        + ", returns <null> instead");
      else
        data_ = null; // runtime is resetting...
    }
    
    data_ = thread_.sendingPort;
    thread_.sendingPort = prevSendingPort_;
    thread_.returnPort = prevReturnPort_;

    if (flagTraceData)
      host.trace(Component.Trace_DATA, this, data_,
        "(end sendReceive())");
        
    return data_;
  }
    
  
  //
  private void ___CONNECTION___() {}
  //
  
  boolean _shadowConnect(Port client_)
  {
    // type mismatch?
    if (getType() == PortType_IN && client_.getType() == PortType_OUT)
      return false;
    else if (getType() == PortType_OUT
      && client_.getType() == PortType_IN)
      return false;

    setType(client_.getType());
    
    if (client_.getType() == PortType_SERVER) {
      if (!_shadowConnectTo(client_, false)) return false;
      return _shadowConnectTo(client_, true);
    }

    if (!isShadow())
      // move to shadow list
      _setShadow(true);
    
    if (inwire == outwire && client_.inwire == client_.outwire) {
      // lump all together
      if (inwire != null) {
        if (client_.inwire != null) {
          inwire.join(client_.inwire);
        }
        else
          inwire.joinInOut(client_);
      }
      else if (client_.inwire != null)
        client_.inwire.shadowJoinInOut(this);
      else
        new Wire().joinInOut(client_).shadowJoinInOut(this);
      return true;
    }
    
    // split in/out if necessary
    if (inwire == outwire && inwire != null
      && client_.inwire != client_.outwire
      && (client_.inwire != null || client_.outwire != null))
      inoutSplit();
    else if (client_.inwire == client_.outwire && client_.inwire != null
      && inwire != outwire && (inwire != null || outwire != null))
      client_.inoutSplit();
    
    if (inwire != null) {
      if (client_.inwire != null)
        inwire.join(client_.inwire);
      else
        inwire.joinIn(client_);
    }
    else if (client_.inwire != null)
      client_.inwire.shadowJoinIn(this);
    else
      new Wire().joinIn(client_).shadowJoinIn(this);
    
    if (outwire != null) {
      if (client_.outwire != null)
        outwire.join(client_.outwire);
      else
        outwire.joinOut(client_);
    }
    else if (client_.outwire != null)
      client_.outwire.shadowJoinOut(this);
    else
      new Wire().joinOut(client_).shadowJoinOut(this);
    
    return true;
  }


  /**
   * Bi-direction connection, consider proxying.
   * Returns true if either direction is set up successfully.
   */
  public final boolean connect(Port peer_) 
  {
    if (host.isAncestorOf(peer_.host)) return _shadowConnect(peer_);
    else if (peer_.host.isAncestorOf(host))
      return peer_._shadowConnect(this);
    
    // server port?
    if (getType() == PortType_SERVER) {
      if (peer_.getType() == PortType_IN
        || peer_.getType() == PortType_SERVER) return false;
      if (!peer_.connectTo(this)) return false;
      if (peer_.inwire == null)
        peer_.inwire = inwire;
      else
        inwire.join(peer_.inwire);
      if (outwire == null)
        outwire = new Wire().joinOut(this);
      return true;
    }
    else if (peer_.getType() == PortType_SERVER) {
      if (getType() == PortType_IN) return false;
      if (!connectTo(peer_)) return false;
      if (inwire == null)
        inwire = peer_.inwire;
      else
        peer_.inwire.join(inwire);
      if (peer_.outwire == null)
        peer_.outwire = new Wire().joinOut(peer_);
      return true;
    }
    
    // type mismatch?
    if (getType() == PortType_IN) {
      if (peer_.getType() == PortType_IN)
        return false;
      return peer_.connectTo(this);
    }
    else if (getType() == PortType_OUT) {
      if (peer_.getType() == PortType_OUT)
        return false;
      return connectTo(peer_);
    }
    else if (peer_.getType() == PortType_OUT)
      return peer_.connectTo(this);
    else if (peer_.getType() == PortType_IN)
      return connectTo(peer_);
    
    if (inwire == outwire && peer_.inwire == peer_.outwire) {
      // lump all together
      if (inwire != null) {
        if (peer_.inwire != null)
          inwire.join(peer_.inwire);
        else
          inwire.joinInOut(peer_);
      }
      else if (peer_.inwire != null)
        peer_.inwire.joinInOut(this);
      else
        new Wire().joinInOut(peer_).joinInOut(this);
      return true;
    }
    
    // split in/out if necessary
    if (inwire == outwire && inwire != null
      && peer_.inwire != peer_.outwire
      && (peer_.inwire != null || peer_.outwire != null))
      inoutSplit();
    else if (peer_.inwire == peer_.outwire && peer_.inwire != null
      && inwire != outwire && (inwire != null || outwire != null))
      peer_.inoutSplit();
    
    // join wires
    if (inwire != null) {
      if (peer_.outwire != null)
        inwire.join(peer_.outwire);
      else
        inwire.joinOut(peer_);
    }
    else if (peer_.outwire != null)
      peer_.outwire.joinIn(this);
    else
      new Wire().joinOut(peer_).joinIn(this);
    
    if (outwire != null) {
      if (peer_.inwire != null)
        outwire.join(peer_.inwire);
      else
        outwire.joinIn(peer_);
    }
    else if (peer_.inwire != null)
      peer_.inwire.joinOut(this);
    else
      new Wire().joinIn(peer_).joinOut(this);

    return true;
  }
  
  /**
   * Disconnects from the given ports.
   */
  public final void connect(Port[] pp_)
  {
    for (int i=0; i<pp_.length; i++)
      connect(pp_[i]);
  }

  /** Attaches the port to the IN wire for receiving data from the wire.  */
  public final void attachIn(Port p_)
  { attachIn(new Port[]{p_}); }
  
  /** Attaches the ports to the IN wire for receiving data from the wire.  */
  public final void attachIn(Port[] pp_)
  {
    Wire wire_ = getInWire();
    if (wire_ == null) {
      wire_ = new Wire();
      wire_.joinIn(this);
    }
    wire_.attach(pp_);
  }

  /** Removes the port from the IN wire.  */
  public final void detachIn(Port p_)
  { detachIn(new Port[]{p_}); }
  
  /** Removes the ports from the IN wire.  */
  public final void detachIn(Port[] pp_)
  {
    Wire wire_ = getInWire();
    if (wire_ != null) wire_.detach(pp_);
  }
  
  /** Attaches the port to the OUT wire for receiving data from the wire.  */
  public final void attachOut(Port p_)
  { attachOut(new Port[]{p_}); }
  
  /** Attaches the ports to the OUT wire for receiving data from the wire.  */
  public final void attachOut(Port[] pp_)
  {
    Wire wire_ = getOutWire();
    if (wire_ == null) {
      wire_ = new Wire();
      wire_.joinOut(this);
    }
    wire_.attach(pp_);
  }

  /** Removes the port from the OUT wire.  */
  public final void detachOut(Port p_)
  { detachOut(new Port[]{p_}); }
  
  /** Removes the port from the OUT wire.  */
  public final void detachOut(Port[] pp_)
  {
    Wire wire_ = getOutWire();
    if (wire_ != null) wire_.detach(pp_);
  }
  
  boolean _shadowConnectTo(Port client_, boolean reverse_)
  {
    // type mismatch?
    if (!reverse_) {
      if (getType() == PortType_OUT || client_.getType() == PortType_OUT)
        return false;
    }
    else
      if (getType() == PortType_IN && client_.getType() == PortType_IN)
        return false;
    
    if (!isShadow())
      // move to shadow list
      _setShadow(true);
    
    if (reverse_) {
      // it's outwire
      if (outwire != null) {
        if (client_.outwire != null)
          outwire.join(client_.outwire);
        else
          outwire.joinOut(client_);
      }
      else if (client_.outwire != null)
        client_.outwire.joinOut(this);
      else
        new Wire().joinOut(client_).joinOut(this);
    }
    else {
      // it's inwire
      if (inwire != null) {
        if (client_.inwire != null)
          inwire.join(client_.inwire);
        else
          inwire.joinIn(client_);
      }
      else if (client_.inwire != null)
        client_.inwire.joinIn(this);
      else
        new Wire().joinIn(client_).joinIn(this);
    }
    return true;
  }
  
  /**
   * Uni-direction connection, consider proxying.
   * Returns true if the connection is set up successfully.
   */
  public final boolean connectTo(Port peer_) 
  {
    if (host.isAncestorOf(peer_.host))
      return _shadowConnectTo(peer_, false);
    else if (peer_.host.isAncestorOf(host))
      return peer_._shadowConnectTo(this, true);
    
    // type mismatch?
    if (getType() == PortType_IN || peer_.getType() == PortType_OUT)
      return false;

    if (outwire == null && peer_.inwire == null)
      return new Wire().joinOut(this).joinIn(peer_) != null;
    else if (outwire != null) {
      if (peer_.inwire != null)
        outwire.join(peer_.inwire);
      else
        outwire.joinIn(peer_);
    }
    else
      peer_.inwire.joinOut(this);
    return true;
  }
  
  // shortcut for probing component
  void _connectTo(Port peer_)
  {
    if (outwire == null && peer_.inwire == null)
      new Wire().joinOut(this).joinIn(peer_);
    else if (outwire != null) {
      if (peer_.inwire != null)
        outwire.join(peer_.inwire);
      else
        outwire.joinIn(peer_);
    }
    else
      peer_.inwire.joinOut(this);
  }
  
  /**
   * Connects to the given ports.
   */
  public final void connectTo(Port[] pp_)
  {
    for (int i=0; i<pp_.length; i++)
      connectTo(pp_[i]);
  }
  
  /**
   * Disconnect with all peers.
   */
  public final void disconnect() 
  { 
    disconnectPeers();
  }
  
  /**
   * Disconnect with all peers and clients on the IN wire.
   */
  public final void disconnectInWire() 
  { 
    if (inwire != null) inwire.disconnect(this);
    inwire = null;
    if (isShadow() && !anyClient())
      _setShadow(false);
  }
  
  /**
   * Disconnect with all peers and clients on the OUT wire.
   */
  public final void disconnectOutWire() 
  { 
    if (outwire != null) outwire.disconnect(this);
    outwire = null;
    if (isShadow() && !anyClient())
      _setShadow(false);
  }
  
  /**
   * Removes peers from the wires attached with this port.
   */
  public final void disconnectPeers()
  {
    if (this == host.infoPort) {
      outwire = null;
      return;
    }
    Port[] peers_ = getPeers();
    if (peers_ == null || peers_.length == 0) return;
    if (inwire != null) {
      inwire.split(peers_);
      if (!inwire.anyPortExcept(this)) inwire = null;
    }
    if (outwire != null) {
      if (outwire != inwire) outwire.split(peers_);
      if (!outwire.anyPortExcept(this)) outwire = null;
    }
  }
  
  public final void disconnectClients()
  {
    Port[] clients_ = getClients();
    if (clients_ == null || clients_.length == 0) return;
    if (inwire != null)
      inwire.split(clients_);
    if (outwire != null && outwire != inwire)
      outwire.split(clients_);
    _setShadow(false);
  }
  
  public final void disconnectWithParent()
  {
    Port[] peers_ = getParentPeers();
    if (peers_ == null || peers_.length == 0) return;
    if (inwire != null) inwire.split(peers_);
    if (outwire != null && outwire != inwire) outwire.split(peers_);
    for (int i=0; i<peers_.length; i++) {
      Port parent_ = peers_[i];
      if (!parent_.anyClient()) parent_._setShadow(false);
    }
  }
  
  /**
   * Splits the "IN" wire/"OUT" wire of this port.
   */
  public synchronized void inoutSplit()
  {
    if (inwire != outwire || inwire == null) return;
    /*
    outwire = (Wire)inwire.clone();
    outwire.inports = inwire.inports;
    inwire.inports = new Port[]{this};
    outwire.outports = new Port[]{this};
    if (inwire.outports != null)
      for (int i=0; i<inwire.outports.length; i++)
        if (inwire.outports[i] == this) inwire.outports[i] = null;
    if (outwire.inports != null)
      for (int i=0; i<outwire.inports.length; i++)
        if (outwire.inports[i] == this) outwire.inports[i] = null;
        else if (outwire.inports[i] != null)
          outwire.inports[i].inwire = outwire;
    */
    inwire.inoutSplit(this);
  }
  
  //
  private void ___MISC___() {}
  //

  public final void exportEvent(String evtName_, Object evtObj_, 
          String evtDescription_)
  {
    doSending(new EventContract.Message(host.getTime(), evtName_,
                toString(), evtObj_, evtDescription_));
  }

  public final void exportEvent(String evtName_, double value_,
          String evtDescription_)
  {
    doSending(new DoubleEventContract.Message(host.getTime(), evtName_,
                toString(), value_, evtDescription_));
  }
  
  public final boolean _isEventExportEnabled()
  { return ((flag & Flag_EVENT_EXPORT) != 0) && anyConnection(); }
  
  /**
   Duplicates the content of the port from <code>source_</code>.
   This method is meant to be invoked by {@link Component}.
   It only duplicates the flag.  ID, groupID and wires are not copied.
   */
  public final void duplicate(Object source_)
  {
    Port that_ = (Port)source_;
    //id = that_.id;
    //groupID = that_.groupID;
    flag = that_.flag;
    //setRemovable(that_.isRemovable()); 
    //inwire = outwire = null; // don't clone wires
  }
  
  String fullpath;
  /** Returns the full path. */
  public final String toString() 
  {
    if (fullpath == null) fullpath = Util.getPortFullID(this);
    return fullpath;
  }
  
  public final String info()
  {
    StringBuffer sb_ = new StringBuffer(Util.getPortFullID(this) + "\n");
    sb_.append("Type: " + getTypeInString() + "\n");
    sb_.append("           Shadow? " + isShadow() + "\n");
    sb_.append("ExecutionBoudnary? " + isExecutionBoundary() + "\n");
    sb_.append("        Removable? " + isRemovable() + "\n");
    sb_.append(" DataTraceEnabled? " + isDataTraceEnabled() + "\n");
    sb_.append(" SendTraceEnabled? " + isSendTraceEnabled() + "\n");
    int i = 0, k = 0;
    StringBuffer tmp_ = new StringBuffer();
    
    /*
    Port[] all_ = _getAllConnectedPorts(true);
    for (int j=0; j<all_.length; j++) {
      Port p_ = all_[j];
      String portid_ = Util.getPortID(p_, host != null && host.parent != null?
                        host.parent: null);
      if (host.isAncestorOf(p_.host))
        tmp_.append(Util.checkConnect(this, p_) + "Client " + ++k + ": " + portid_ + "\n");
      else
        sb_.append(Util.checkConnect(this, p_) + "Peer " + ++i + ": " + portid_ + "\n");
    }
    if (i + k == 0) sb_.append("No peer and client.\n");
    else if (i==0) sb_.append("No peer.\n" + tmp_);
    else if (k==0) sb_.append("No client.\n");
    else sb_.append(tmp_);
    */
    if (inwire == outwire) {
      if (inwire == null) sb_.append("No connection.\n");
      else
        sb_.append("one wire for in/out: " + inwire + "\n");
    }
    else {
      if (inwire != null)
        sb_.append("inwire: " + inwire + "\n");
      if (outwire != null)
        sb_.append("outwire:" + outwire + "\n");
    }
    return sb_.toString();
  }
}



